<script>
import * as d3 from 'd3'
// import DetailPanel from "./DetailPanel.vue";
import axios from "axios";
import DetailPanel from "@/components/DetailPanel.vue";
export default {
  components: {DetailPanel},
  data () {
    return{
      links:[],
      nodes:[],
      nodesName:[],
      linksName:[],
      simulation:null,
      testGraph:{
        "nodes": [],
        "links": []
      },
      currentNode: null,
      currentType: null
    }
  },
  created() {
    var _this = this
    // 添加完整的基础 URL
      this.$axios.get(`/all`)
    // this.$axios.get(`/person/${encodeURIComponent('Rob Reiner')}`)
      .then(response => {
        console.log(response)
        if (response.data && Array.isArray(response.data)) {
          // 构造初始数据
          const initialData = {
            nodes: response.data,
            links: []
          };
          _this.initGraph(initialData);
        }
      })
      .catch(error => {
        console.error('请求失败:', (error.response && error.response.data) || error.message);
      });
  },
  methods: {
    initGraph(data){
      var _this = this
      // Specify the dimensions of the chart.
      const width = 1200;  // 增加宽度
      const height = 800;  // 增加高度

      // Specify the color scale.
      const color = d3.scaleOrdinal(d3.schemeCategory10);

      // The force simulation mutates links and nodes, so create a copy
      // so that re-evaluating this cell produces the same result.
      const links = data.links.map(d => Object.create(d));
      const nodes = data.nodes.map(d => Object.create(d));

      // Create a simulation with several forces.
      _this.simulation = d3.forceSimulation(nodes)
        .force("link", d3.forceLink(links).id(d => d.id).distance(150))  // 增加节点间距离
        .force("collide", d3.forceCollide().radius(() => 40))  // 增加碰撞半径
        .force("charge", d3.forceManyBody().strength(-100))  // 调整排斥力
        .force("center", d3.forceCenter(width / 2, height / 2))
        .on("tick", ticked);

      // Create the SVG container.
      const svg = d3.select(".container")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .attr("viewBox", [0, 0, width, height])
        .attr("style", "max-width: 100%; height: auto; background-color: #ffffff;");

      // 添加缩放功能
      const zoom = d3.zoom()
        .scaleExtent([0.5, 3])  // 限制缩放范围
        .on("zoom", (event) => {
          g.attr("transform", event.transform);
        });

      svg.call(zoom);

      const g = svg.append("g");

      // 添加箭头标记
      const marker = g.append("marker")
        .attr("id", "direction")
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 20)  // 调整箭头位置
        .attr("refY", 0)
        .attr("markerWidth", 6)  // 减小箭头大小
        .attr("markerHeight", 6)
        .attr("orient", "auto")
        .append("path")
        .attr("d", "M0,-5L10,0L0,5")
        .attr("fill", "#666");  // 调整箭头颜色

      // Add a line for each link, and a circle for each node.
      _this.links = g.append("g")
        .attr("stroke", "#999")
        .attr("stroke-opacity", 0.6)
        .attr("marker-end", "url(#direction)")
        .selectAll("path")
        .data(links)
        .join("path")
        .attr("id", d => `${d.source.id || d.source}_${d.relationship || ''}_${d.target.id || d.target}`)
        .attr("stroke-width", d => Math.sqrt(d.value) * 1.2);  // 减小线条粗细

      // 添加关系标签
      _this.linksName = g.append("g")
        .selectAll("text")
        .data(links)
        .join("text")
        .style("text-anchor", "middle")
        .style("font-size", "11px")  // 减小字体大小
        .style("fill", "#666")
        .append("textPath")
        .attr("xlink:href", d => `#${d.source.id || d.source}_${d.relationship || ''}_${d.target.id || d.target}`)
        .attr("startOffset", "50%")
        .text(d => d.relationship || '');

      // 添加节点
      _this.nodes = g.append("g")
        .attr("stroke", "#fff")
        .attr("stroke-width", 1.5)  // 减小节点边框宽度
        .selectAll("circle")
        .data(nodes, d => d.id)
        .join("circle")
        .on("click", (event, d) => this.select(d))
        .attr("r", 6)  // 减小节点大小
        .attr("fill", d => color(d.group))
        .style("cursor", "pointer");

      // 添加节点标签
      _this.nodesName = g.append("g")
        .selectAll("text")
        .data(nodes)
        .join("text")
        .text(d => d.id)
        .style("font-size", "11px")  // 减小字体大小
        .style("fill", "#333")
        .attr("dx", function() {
          return this.getBoundingClientRect().width / 2 * (-1);
        })
        .attr("dy", 15)  // 减小标签与节点的距离
        .attr("class", "nodeName");

      // 添加节点提示
      _this.nodes.append("title")
        .text(d => d.id);

      // 添加拖拽行为
      _this.nodes.call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

      // Set the position attributes of links and nodes each time the simulation ticks.
      function ticked() {
        _this.links
          .attr("d", d => {
            const sourceX = typeof(d.source) === 'object' ? d.source.x : d.source.x;
            const sourceY = typeof(d.source) === 'object' ? d.source.y : d.source.y;
            const targetX = typeof(d.target) === 'object' ? d.target.x : d.target.x;
            const targetY = typeof(d.target) === 'object' ? d.target.y : d.target.y;
            return `M${sourceX},${sourceY}L${targetX},${targetY}`;
          });

        _this.nodes
          .attr("cx", d => d.x)
          .attr("cy", d => d.y);

        _this.nodesName
          .attr("x", d => d.x)
          .attr("y", d => d.y);
      }

      // Reheat the simulation when drag starts, and fix the subject position.
      function dragstarted(event) {
        if (!event.active) _this.simulation.alphaTarget(0.3).restart();
        event.subject.fx = event.subject.x;
        event.subject.fy = event.subject.y;
      }

      // Update the subject (dragged node) position during drag.
      function dragged(event) {
        event.subject.fx = event.x;
        event.subject.fy = event.y;
      }

      // Restore the target alpha so the simulation cools after dragging ends.
      // Unfix the subject position now that it's no longer being dragged.
      function dragended(event) {
        if (!event.active) _this.simulation.alphaTarget(0);
        event.subject.fx = null;
        event.subject.fy = null;
      }
    },

    color(d) {
      const scale = d3.scaleOrdinal(d3.schemeCategory10);
      return scale(d.group);
    },

    select(d){
      var _this = this
      let data = {}
      if (d && d.obj) {
        for (var i in d.obj) {
          let ifArray = Array.isArray(d.obj[i])
          if(!ifArray){
            data[i] = d.obj[i]
          }
        }
      } else {
        // 如果没有 obj 属性，直接使用节点数据
        data = { ...d }
      }
      if (_this.$refs.detailPanel) {
        _this.$refs.detailPanel.currentNode = data
        _this.$refs.detailPanel.ifShow = true
      }
    },

    updateGraph(data, currentNode, currentType){
      var _this = this
      console.log('更新图数据:', data);
      console.log('当前节点:', currentNode);
      console.log('当前关系类型:', currentType);

      // 保存当前节点和关系类型
      _this.currentNode = currentNode;
      _this.currentType = currentType;

      // 检查数据格式
      if (!data) {
        console.warn('No data provided for graph update');
        return;
      }

      // 如果传入的是数组，假设它是节点数组
      if (Array.isArray(data)) {
        data = {
          nodes: data,
          links: []
        };
      }

      // 确保数据格式正确
      if (!Array.isArray(data.nodes)) {
        console.warn('Invalid data format for graph update:', data);
        return;
      }

      // 合并现有节点和新节点
      const existingNodes = _this.simulation ? _this.simulation.nodes() : [];
      console.log('现有节点:', existingNodes);

      const existingNodeIds = new Set(existingNodes.map(d => d.id));
      console.log('现有节点ID:', Array.from(existingNodeIds));

      // 只添加新的节点
      const newNodes = data.nodes.filter(d => !existingNodeIds.has(d.id));
      console.log('新节点:', newNodes);

      const allNodes = [...existingNodes, ...newNodes];
      console.log('所有节点:', allNodes);

      // 创建新的关系
      const newLinks = [];
      if (newNodes.length > 0 && currentNode) {
        console.log('创建新关系，当前节点:', currentNode);
        console.log('创建新关系，关系类型:', currentType);

        // 为每个新节点创建与当前选中节点的关系
        newNodes.forEach(node => {
          const link = {
            source: currentNode.name,
            target: node.id,
            relationship: currentType,
            value: 1
          };
          console.log('创建新关系:', link);
          newLinks.push(link);
        });
      }

      // 合并现有关系和新关系
      const existingLinks = _this.simulation ? _this.simulation.force("link").links() : [];
      console.log('现有关系:', existingLinks);

      const allLinks = [...existingLinks, ...newLinks];
      console.log('所有关系:', allLinks);

      // 更新链接
      if (_this.links) {
        console.log('更新链接元素');
        _this.links = _this.links
          .data(allLinks)
          .join("path")
          .attr("id", d => `${d.source}_${d.relationship || ''}_${d.target}`)
          .attr("stroke", "#999")
          .attr("stroke-opacity", 0.6)
          .attr("marker-end", "url(#direction)")
          .attr("stroke-width", d => Math.sqrt(d.value) * 1.5);
      }

      // 更新节点
      if (_this.nodes) {
        console.log('更新节点元素');
        _this.nodes = _this.nodes
          .data(allNodes, d => d.id)
          .join("circle")
          .on("click", (event, d) => this.select(d))
          .attr("r", 8)
          .attr("fill", d => this.color(d.group))
          .style("cursor", "pointer");
      }

      // 更新节点标签
      if (_this.nodesName) {
        console.log('更新节点标签');
        _this.nodesName = _this.nodesName
          .data(allNodes)
          .join("text")
          .text(d => d.id)
          .style("font-size", "12px")
          .style("fill", "#333")
          .attr("dx", function() {
            return this.getBoundingClientRect().width / 2 * (-1);
          })
          .attr("dy", 20)
          .attr("class", "nodeName");
      }

      // 更新关系标签
      if (_this.linksName) {
        console.log('更新关系标签');
        _this.linksName = _this.linksName
          .data(allLinks)
          .join("text")
          .style("text-anchor", "middle")
          .style("font-size", "12px")
          .style("fill", "#666")
          .append("textPath")
          .attr("xlink:href", d => `#${d.source}_${d.relationship || ''}_${d.target}`)
          .attr("startOffset", "50%")
          .text(d => d.relationship || '');
      }

      // 重新启动模拟
      if (_this.simulation) {
        console.log('重启力导向模拟');
        _this.simulation.nodes(allNodes);
        _this.simulation.force("link").links(allLinks);
        _this.simulation.alpha(1).restart();
      }

      // 更新 ticked 函数
      _this.simulation.on("tick", () => {
        _this.links
          .attr("d", d => {
            const sourceX = typeof(d.source) === 'object' ? d.source.x : d.source.x;
            const sourceY = typeof(d.source) === 'object' ? d.source.y : d.source.y;
            const targetX = typeof(d.target) === 'object' ? d.target.x : d.target.x;
            const targetY = typeof(d.target) === 'object' ? d.target.y : d.target.y;
            return `M${sourceX},${sourceY}L${targetX},${targetY}`;
          });

        _this.nodes
          .attr("cx", d => d.x)
          .attr("cy", d => d.y);

        _this.nodesName
          .attr("x", d => d.x)
          .attr("y", d => d.y);
      });
    }
  }
}
</script>

<template>
  <div class="container">
    <detail-panel ref="detailPanel" @update="updateGraph"></detail-panel>
  </div>
</template>

<style scoped>
.container {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>
